using System;
using System.Collections.Generic;
using System.Text;

namespace VerticalProcesses
{
    /// <summary>
    /// Basic soil unit
    /// </summary>
    public abstract class SoilType
    {

        // public properties

        public string SName { get; set; }
        public double Infiltration { get; set; }
        public double SoilMoisture { get; set; }
        public double SoilEvaporation { get; set; }
        public double WaterRelease { get; set; }

        public abstract void Update(DateTime TimeStep, double ETact, double swe, double crownClosure);

        # region Implementions of different Soiltypes
        /// <summary>
        /// Add your soiltype implemention here
        /// </summary>
    }
    /// <summary>
    /// This is a simmple soil based on the HBV-EC soil implementation
    /// </summary>
    public class HBVECSoil : SoilType
    {
        private double FC;
        private double BETA;
        private double LP;


        public override void Update(DateTime TimeStep, double ETact, double swe, double crownClosure)
        {
            WaterRelease = 0.0;
            SoilEvaporation = 0.0;

            if (Infiltration > 0.0)
            {

                double potEvaporationPerMM = ETact / Infiltration;
                double infiltrationResidual = Infiltration - Math.Floor(Infiltration);
                double potEvaporationResidual = infiltrationResidual * potEvaporationPerMM;

                //Add 1mm (or residual) infiltration to soil at a time
                //Compute water release and actual evaporation
                double increment = 0.0;
                while (increment < Infiltration)
                {

                    //defaults assume increment < floor(soilInfiltration)
                    double actualInfiltration = 1.0;
                    double actualPotEvaporation = potEvaporationPerMM;

                    if (increment >= Math.Floor(Infiltration))
                    {
                        actualInfiltration = infiltrationResidual;
                        actualPotEvaporation = potEvaporationResidual;
                    }

                    double waterReleaseFraction = Math.Pow(SoilMoisture / FC, BETA);

                    double thisWaterRelease = waterReleaseFraction * actualInfiltration;
                    actualInfiltration = (1.0 - waterReleaseFraction) * actualInfiltration;
                    //	waterRelease += waterReleaseFraction * actualInfiltration;
                    //  soilMoisture += (1.0-waterReleaseFraction) * actualInfiltration;

                    if (SoilMoisture + actualInfiltration > FC)
                    {
                        thisWaterRelease += SoilMoisture + actualInfiltration - FC;
                        actualInfiltration = FC - SoilMoisture;
                        //waterRelease += soilMoisture - FC;
                        //soilMoisture = FC;
                    }

                    WaterRelease += thisWaterRelease;
                    SoilMoisture += actualInfiltration;

                    //Evaporate
                    double thisEvaporation = CalculateSoilEvaporation(actualPotEvaporation, SoilMoisture, swe, crownClosure);
                    SoilEvaporation += thisEvaporation;
                    SoilMoisture -= thisEvaporation;
                    increment += 1.0;

                }

            }
            else // no infiltration
            {
                //evaporate
                double thisEvaporation = CalculateSoilEvaporation(ETact, SoilMoisture, swe, crownClosure);
                SoilEvaporation += thisEvaporation;
                SoilMoisture -= thisEvaporation;

            }

        }
        /// <summary>
        /// Calculates and returns soil evaporation
        /// </summary>
        /// <param name="potEvaporation"></param>
        /// <returns></returns>
        private double CalculateSoilEvaporation(double potEvaporation, double soilMoisture, double swe, double crownClosure)
        {
            if (swe > 0.0 && crownClosure == 0.0)
                return 0.0; //no evaporation through the snow cover...except for forest

            double actualEvaporation = potEvaporation;

            if (soilMoisture < LP * FC) //evaporation is supply limited
                actualEvaporation = potEvaporation * (soilMoisture / (LP * FC));

            return Math.Min(soilMoisture, actualEvaporation);
        }
        public HBVECSoil(double FC, double LP, double BETA)
        {

            this.FC = FC;
            this.LP = LP;
            this.BETA = BETA;
        }
    }
    /// <summary>
    /// A very simple conceptual implementation of a wetland that 
    /// releases no water but just evaporates
    /// </summary>
    public class WetLand : SoilType
    {
        private double BETA;


        public override void Update(DateTime TimeStep, double ETact, double swe, double crownClosure)
        {

            SoilEvaporation = Math.Min(ETact, SoilMoisture);
            SoilMoisture += Infiltration - SoilEvaporation;
            WaterRelease = 0.0;

        }
        public WetLand(double BETA)
        {
            BETA = BETA;
        }
    }

    
        #endregion
}

